package net.organizer.ui.actionhandler;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;

import net.organizer.dao.Domain;
import net.organizer.dao.Org;
import net.organizer.dao.OrgPerson;
import net.organizer.dao.Person;

import org.json.JSONArray;
import org.json.JSONObject;

import com.googlecode.objectify.NotFoundException;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;

public class LoadSampleDataActionHandler extends ActionHandlerBase {

	// --------------------------------------------------------------------------------------------------

	private static final Logger logger = Logger.getLogger(LoadSampleDataActionHandler.class.getName());

	// --------------------------------------------------------------------------------------------------
	@Override
	public void handleAction(final HttpServletRequest req, final JSONObject respJSONObject) throws Exception {
		logger.fine("handleAction");
		JSONArray jSONArray = new JSONArray();
		Objectify ofy = ObjectifyService.beginTransaction();
		Domain domain = null;
		try {
			try {
				// the following ofy.get() creates the key then executes the
				// get.
				domain = ofy.get(Domain.class, "domain1");
			} catch (NotFoundException e) {
				logger.fine("have NotFoundException for domain with id == 1, will put");
				domain = new Domain("domain1", "domain desc1");
				Object domainPutResult = ofy.put(domain);
				logger.fine(String.format("domainPutResult.getClass().getName()): %s ", domainPutResult.getClass()
						.getName()));
			}
			ofy.getTxn().commit();
		} finally {
			// error will hopefully result in exception that will be handled in
			// AjaxDispatcher
			if (ofy.getTxn().isActive()) {
				ofy.getTxn().rollback();
			}
		}

		// start a new transaction
		// no auto-retries, just get out of failure
		ofy = ObjectifyService.beginTransaction();
		try {


			List<OrgPerson> oldOrgPersons = ofy.query(OrgPerson.class).ancestor(domain.getKey()).list();
			logger.fine(String.format("have %d oldORgPersons in the domain", oldOrgPersons.size()));
			for (OrgPerson orgPerson : oldOrgPersons) {
				logger.fine(String.format("old orgPerson: %s", orgPerson));
			}
			ofy.delete(oldOrgPersons);
			jSONArray.put(String.format("completed delete of %d old OrgPersons.", oldOrgPersons.size()));
			
			List<Person> oldPersons = ofy.query(Person.class).ancestor(domain.getKey()).list();
			logger.fine(String.format("have %d oldPersons in the domain", oldPersons.size()));
			for (Person person : oldPersons) {
				logger.fine(String.format("old person: %s", person));
			}

			long startTime = System.currentTimeMillis();
			ofy.delete(oldPersons);

			ofy.getTxn().commit();
			ofy = ObjectifyService.beginTransaction();

			jSONArray.put(String.format("completed delete of %d old Persons in %d milliseconds.", oldPersons.size(),
					System.currentTimeMillis() - startTime));
			startTime = System.currentTimeMillis();
			oldPersons = ofy.query(Person.class).ancestor(domain.getKey()).list();
			logger.fine(String.format("have %d oldPersons after delete", oldPersons.size()));

			List<Org> oldOrgs = ofy.query(Org.class).ancestor(domain.getKey()).list();
			logger.fine(String.format("have %d oldOrgs...key: %s", oldOrgs.size(), domain.getKey()));
			startTime = System.currentTimeMillis();

			ofy.delete(oldOrgs);

			ofy.getTxn().commit();
			ofy = ObjectifyService.beginTransaction();

			jSONArray.put(String.format("completed delete of %d old orgs in %d milliseconds.", oldOrgs.size(),
					System.currentTimeMillis() - startTime));
			startTime = System.currentTimeMillis();
			oldOrgs = ofy.query(Org.class).ancestor(domain.getKey()).list();
			logger.fine(String.format("have %d oldOrgs after delete", oldOrgs.size()));

			Date date = new SimpleDateFormat("MM/dd/yy").parse("05/18/89");
			
			int personsLength = 25;
			Person[] persons = new Person[personsLength];
			int j = 0;
			for (int i = 0; i < personsLength; i++) {
				j = i + 1;
				persons[i] = new Person(new Long(j), 1, new Long(date.getTime()), domain, "firstName" + j, "googleUserid" + j,
						"lastName" + j, "phoneNumberCell" + j, "phoneNumberHome" + j, "phoneNumberOffice" + j);
			}
			Object putResult = ofy.put(persons);

			ofy.getTxn().commit();
			ofy = ObjectifyService.beginTransaction();

			jSONArray.put(String.format("completed put of %d Persons in %d milliseconds.", personsLength,
					System.currentTimeMillis() - startTime));
			startTime = System.currentTimeMillis();
			logger.fine(String.format("putResult putResult.getClass().getName()): %s ", putResult.getClass().getName()));
			LinkedHashMap linkedHashMap = (LinkedHashMap) putResult;
			logger.fine(String.format("putResult linkedHashMap.size(): %d ", linkedHashMap.size()));

			// Have Key, Person
			for (Object hashMapKey : linkedHashMap.keySet()) {
				logger.fine(String.format("put result hashMapKey.getClass().getName()): %s, toString: %s", hashMapKey
						.getClass().getName(), hashMapKey));
				Object hashMapValue = linkedHashMap.get(hashMapKey);
				logger.fine(String.format("put result hashMapValue.getClass().getName()): %s, toString: %s", hashMapKey
						.getClass().getName(), hashMapValue));
			}

			// add the parents
			int orgsLength = 5;
			Org[] orgs = new Org[orgsLength];
			j = 0;
			// insert top level orgs
			for (int i = 0; i < orgsLength; i++) {
				j = i + 1;
				orgs[i] = new Org(null, "desc...parent" + j, domain, "name." + j, "namespacename" + j);
			}
			ofy.put(orgs);
			ofy.getTxn().commit();
			ofy = ObjectifyService.beginTransaction();

			jSONArray.put((String.format("completed put of %d parent Orgs in %d milliseconds.", orgsLength,
					System.currentTimeMillis() - startTime)));
			startTime = System.currentTimeMillis();
			// the following query does not seem to execute in the transactional
			// context
			// That is, it doesn't see the parents that were just inserted,
			// unless we
			// do the commit. Does this make sense?
			List<Org> parentOrgs = ofy.query(Org.class).ancestor(domain.getKey()).list();
			logger.fine(String.format("have %d parentOrgs after put", parentOrgs.size()));

			List<Org> childOrgs = new LinkedList<Org>();
			int k = 0;
			for (Org parentOrg : parentOrgs) {
				childOrgs.add(new Org(parentOrg, "desc....child" + k, domain, parentOrg.getName() + "." + k,
						"namespacename" + ++k));
				childOrgs.add(new Org(parentOrg, "desc....child" + k, domain, parentOrg.getName() + "." + k,
						"namespacename" + ++k));
				childOrgs.add(new Org(parentOrg, "desc....child" + k, domain, parentOrg.getName() + "." + k,
						"namespacename" + ++k));
			}
			ofy.put(childOrgs);
			jSONArray.put((String.format("completed put of %d child Orgs in %d milliseconds.", childOrgs.size(),
					System.currentTimeMillis() - startTime)));

			ofy.getTxn().commit();

			logger.info("add another layer of children");
			ofy = ObjectifyService.beginTransaction();
			parentOrgs = ofy.query(Org.class).ancestor(domain.getKey()).list();
			logger.fine(String.format("have %d parentOrgs after put", parentOrgs.size()));

			childOrgs = new LinkedList<Org>();
			k = 0;
			for (Org parentOrg : parentOrgs) {
				if (parentOrg.getDesc().contains("child")) {
					childOrgs.add(new Org(parentOrg, "desc....childchild" + k, domain, parentOrg.getName() + "." + k,
							"namespacename" + ++k));
					childOrgs.add(new Org(parentOrg, "desc....childchild" + k, domain, parentOrg.getName() + "." + k,
							"namespacename" + ++k));
					childOrgs.add(new Org(parentOrg, "desc....childchild" + k, domain, parentOrg.getName() + "." + k,
							"namespacename" + ++k));
				}
			}
			ofy.put(childOrgs);
			jSONArray.put((String.format("completed put of %d child child Orgs in %d milliseconds.", childOrgs.size(),
					System.currentTimeMillis() - startTime)));
			ofy.getTxn().commit();

			logger.info("add another layer of children");
			ofy = ObjectifyService.beginTransaction();
			parentOrgs = ofy.query(Org.class).ancestor(domain.getKey()).list();
			logger.fine(String.format("have %d parentOrgs after put", parentOrgs.size()));

			childOrgs = new LinkedList<Org>();
			k = 0;
			for (Org parentOrg : parentOrgs) {
				if (parentOrg.getDesc().contains("childchild")) {
					childOrgs.add(new Org(parentOrg, "desc....childchildchild" + k, domain, parentOrg.getName() + "."
							+ k, "namespacename" + ++k));
					childOrgs.add(new Org(parentOrg, "desc....childchildchild" + k, domain, parentOrg.getName() + "."
							+ k, "namespacename" + ++k));
					childOrgs.add(new Org(parentOrg, "desc....childchildchild" + k, domain, parentOrg.getName() + "."
							+ k, "namespacename" + ++k));
				}
			}
			ofy.put(childOrgs);
			jSONArray.put((String.format("completed put of %d child child child Orgs in %d milliseconds.",
					childOrgs.size(), System.currentTimeMillis() - startTime)));
			ofy.getTxn().commit();
			
			// add some persons to some orgs
			ofy = ObjectifyService.beginTransaction();

			List<Person> personsForOrgs = ofy.query(Person.class).ancestor(domain.getKey()).list();
			logger.fine(String.format("have %d personsForOrgs in the domain", personsForOrgs.size()));
			
			List<Org> orgsToGetPersons = ofy.query(Org.class).ancestor(domain.getKey()).list();
			logger.fine(String.format("have %d orgsToGetPersons...key: %s", orgsToGetPersons.size(), domain.getKey()));
			
			// add some people to the first org
			List<OrgPerson> orgPersons = new LinkedList<OrgPerson>();
			int NUMBER_OF_PEOPLE_TO_ADD = 10;
			for(int i = 0; i < NUMBER_OF_PEOPLE_TO_ADD; i++){
				orgPersons.add(new OrgPerson(orgsToGetPersons.get(0), personsForOrgs.get(i), domain));
			}
			// add some people to the second org
			for(int i = 5; i < NUMBER_OF_PEOPLE_TO_ADD; i++){
				orgPersons.add(new OrgPerson(orgsToGetPersons.get(1), personsForOrgs.get(i), domain));
			}
			ofy.put(orgPersons);
			jSONArray.put((String.format("completed put of %d org persons.", orgPersons.size())));
			ofy.getTxn().commit();

			jSONArray.put("load sample data is complete");
			respJSONObject.put("messages", jSONArray);
		} finally {
			if (ofy.getTxn().isActive()) {
				ofy.getTxn().rollback();
			}
		}

	}
}